home *** CD-ROM | disk | FTP | other *** search
- //
- // MME+Symbols.m -- merge engine symbol table and variable handlers
- // Written by Don Yacktman Copyright (c) 1995 by Don Yacktman.
- // Version 1.0. All rights reserved.
- // This notice may not be removed from this source code.
- //
- // This object is included in the MiscKit by permission from the author
- // and its use is governed by the MiscKit license, found in the file
- // "LICENSE.rtf" in the MiscKit distribution. Please refer to that file
- // for a list of all applicable permissions and restrictions.
- //
-
-
- #import <misckit/misckit.h>
- #import <misckit/MiscMergeEngine.h>
- #import <misckit/MiscMergeTemplate.h>
- #import <misckit/MiscIfStack.h>
-
-
- @implementation MiscMergeEngine (Symbols)
-
- // Handling the local symbol table
-
- - resetSymbolTable
- /*" Clears the local symbol table. Returns self.
- "*/
- { // we don't free values--this could leak memory
- if (symbolTable) {
- [symbolTable freeObjects];
- [symbolTable free];
- }
- symbolTable = [[MiscDictionary alloc] init];
- return self;
- }
-
- - (MiscString *)symbolForKey:(MiscString *)name
- /*" Attempts to find a symbol in the symbol table that corresponds to
- %{name}. If found, the value for that symbol is returned, if not, then
- nil is returned. The search is conducted through the local symbol
- table, through all parent merges' symbol tables, and then through the
- global symbol table.
- "*/
- { // if not available locally, we check the global table.
- if ([symbolTable isKey:name]) {
- // we have it, so return that.
- return [symbolTable valueForKey:name];
- } else {
- if (parentMerge) {
- // if parent doesn't have it, it will try it's
- // parent, backing up until the global table is hit.
- return [parentMerge symbolForKey:name];
- }
- }
- // if the parent doesn't exist and we don't
- // have it, then we check the globals.
- return [[self class] globalSymbolForKey:name];
- }
-
- - setSymbol:(MiscString *)name toValue:(MiscString *)value
- /*" Adds the symbol %{name} to the local symbol table with %{value} as
- its value. Returns self.
- "*/
- {
- [value squashSpaces]; // just to be safe
- [symbolTable insertKey:name value:value];
- return self;
- }
-
- - (MiscString *)getField:(MiscString *)fieldName
- /*" Attempts to resolve a field name. If found in the merge dictionary,
- then the value in the dictionary is returned. If not found there, then
- a search through the symbol tables is conducted. If there are no local
- or global symbols named %{fieldName} then %{fieldName} is returned. If
- there are local or global symbols, however, then an attempt is made to
- resolve their values to a key in the merge dictionary. If the local or
- global symbols exist, but cannot be resolved into values in the merge
- dictionary, then they local/global value is returned.
-
- This complex search allows aliases to be created for various merge fields
- so that they may be accessed by different names. It also allows
- redirection--if a field is missing in a particular merge, the global
- or local symbol tables can suggest another field to use in its place.
- Finally, it allows setting of default values in the local or global
- symbol table. If the field is missing from the merge dictionary, then a
- default stored in the symbol tables can be used.
- "*/
- {
- MiscString *fieldString = nil;
- MiscString *fieldString2 = nil;
-
- [fieldName squashSpaces];
- // if in the merge record, return it
- if ([dictionary isKey:fieldName]) {
- return [dictionary valueForKey:fieldName];
- }
-
- fieldString = [self symbolForKey:fieldName];
- // while loop allows for indirection
- while (fieldString) {
- if ([dictionary isKey:fieldString]) {
- return [dictionary valueForKey:fieldString];
- }
- fieldString2 = fieldString;
- fieldString = [self symbolForKey:fieldString];
- }
- if (fieldString2) return fieldString2;
-
- // Following if condition is a temporary fix for leaving out spurious
- // "blank" fields at the end of the merge template. It should really
- // only have to check the state of "leaveDelimiters", not "fieldName".
- if (leaveDelimiters && (fieldName != NULL) && (![fieldName emptyString])) {
- MiscString *output = [MiscString new];
- [output setFromFormat:"%c%s%c",
- [MiscMergeTemplate startFieldDelimiter],
- [fieldName stringValue],
- [MiscMergeTemplate endFieldDelimiter]];
- return output; // these will LEAK, since not coming from dictionary.
- // (other strings returned by this method shouldn't be freed!)
- // No way around it until we get the foundation kit. :-(
- }
- return fieldName; // treat as a literal if not found
- }
-
- - setLeaveDelimiters:(BOOL)aFlag
- /*" Tells engine how to handle unresolveable merge fields. If %{aFlag}
- is YES, then the merge delimiters will be put around the field name and
- that will be returned. If NO, then the field name will be returned
- without the delimiters on. (That is the default.) Returns self.
- "*/
- {
- leaveDelimiters = aFlag;
- return self;
- }
-
- - (BOOL)leaveDelimiters
- /*" Returns how the engine is set to handle unresolveable merge fields.
- "*/
- {
- return leaveDelimiters;
- }
-
- - resetVariables
- /*" Empties out the merge variables. Returns self.
- "*/
- { // we don't free values--this could leak memory
- if (variables) {
- [variables freeObjects];
- [variables free];
- }
- variables = [[MiscDictionary alloc] init];
- return self;
- }
-
- - getVariableNamed:(MiscString *)name
- /*" Returns the merge variable named %{name}. Returns nil if not found.
- "*/
- {
- return [variables valueForKey:name];
- }
-
- - setVariableNamed:(MiscString *)name to:aValue
- /*" Sets the merge variable named %{name} to %{aValue}. Returns self.
- "*/
- {
- [variables insertKey:name value:aValue];
- return self;
- }
-
- // This is a "special" variable available to all mergers; we make
- // sure that it exists. Commands can obviously still install their
- // own variables as well...
- - ifStack
- /*" Returns the special “if stack” merge variable, creating it if
- necessary. The “if stack” is used by the if/else/endif commands
- and also used to control turning the output of the merge on and off.
- "*/
- {
- static id ifStackKey = nil;
- id ifStack;
-
- if (!ifStackKey) ifStackKey = [MiscString newWithString:"_Misc_ifStack"];
- ifStack = [self getVariableNamed:ifStackKey];
- if (!ifStack) {
- ifStack = [[MiscIfStack alloc] init];
- [self setVariableNamed:ifStackKey to:ifStack];
- }
- return ifStack;
- }
-
- @end
-